home *** CD-ROM | disk | FTP | other *** search
- Z.009: Vorneweg
- Z.028: Inhalt
- Z.186: Assemblerbenutzung
- Z.410: Compiler, Standardprozeduren und Laufzeitsystem
- Z.659: Literatur
- Z.690: Zu guter Letzt
-
-
- Vorneweg: 6. März '90
- ======================================================================
-
- HK_LIB_1.0 darf und soll unter folgenden Bedingungen weitergegeben
- werden:
-
- 1. Der Ordner HK_LIB_1.0 muß immer vollständig kopiert
- werden, d.h. mit Quelltexten, Objektdateien und diesem
- README.
-
- 2. Für die Weitergabe dürfen keine Gebühren verlangt
- werden, außer den üblichen ( manchmal reichlich hohen )
- Kopier- und Diskettenkosten.
-
- 3. HK_LIB_1.0 darf nicht ohne meine Genehmigung mit kommer-
- ziellen Produkten weitergegeben oder in kommerziellen
- Produkten verwendet werden.
-
-
- Inhalt:
- ========================================================================
-
- HK_LIB ist eine Sammlung unterschiedlicher Module für das Public-Domain
- Modulasystem 'LPR-Modula'. Mit ein paar kleinen Anpassungen läßt es sich
- wahrscheinlich aber leicht auf andere Modulasysteme übertragen, da sämt-
- liche Quellcodes vorhanden sind.
-
- Auf der Diskette sollten folgende Dateien vorhanden sein:
-
- \HK_LIB_1.0
-
- \DEF_MOD * Ordner mit den Quelltexten
-
- ASCII .DEF/MOD
- CHARS .DEF/MOD
- CONVERTS.DEF/MOD
- LONGSETS.DEF/MOD
- MACHINE .DEF/MOD
- MATHBASE.DEF/MOD
- MEMORY .DEF/MOD
- QUEUES .DEF/MOD
- SCREEN .DEF/MOD
- STACKS .DEF/MOD
- STRINGS .DEF/MOD
- TIMER .DEF/MOD
- TRAPDEFS.DEF/MOD
- XSTRINGS.DEF/MOD
-
- \SBM_OBM * Ordner mit den Objektdateien
-
- ASCII .SBM/OBM
- CHARS .SBM/OBM
- CONVERTS.SBM/OBM
- LONGSETS.SBM/OBM
- MACHINE .SBM/OBM
- MATHBASE.SBM/OBM
- MEMORY .SBM/OBM
- QUEUES .SBM/OBM
- SCREEN .SBM/OBM
- STACKS .SBM/OBM
- STRINGS .SBM/OBM
- TIMER .SBM/OBM
- TRAPDEFS.SBM/OBM
- XSTRINGS.SBM/OBM
-
- README * Diese Datei
-
-
- Hier soll nur ein kleiner Überblick gegeben werden, die genaue Beschrei-
- bung der einzelnen Routinen steht in den relativ ausführlich kommentierten
- Definitionsmodulen, im Zweifelsfall hilft sicher auch ein Blick in die
- Implementationsmodule, in deren Kopftexten z.T. ausführliche Erläuterungen
- zu den verwendeten Verfahren zu finden sind.
-
-
- ASCII: --------------------------------------------------------------
- Das ist ein reines Datenmodul mit Konstanten für ASCII-Codes und
- deutsche Sonderzeichen.
-
- Chars: --------------------------------------------------------------
- Hier steht alles, was mit Einzelzeichen zu tun hat, wobei ich
- mich bemüht habe, die deutschen Umlaute weitgehend zu berücksich-
- tigen; so gibt es neben Testroutinen ("IsDigit", "IsUmlaut" usw. )
- auch Konvertierungsfunktionen ("LowerCaseGerman","ToAtariGerman"..).
- Die Funktionen sind teilweise in Assembler programmiert, was aber
- nicht die erhoffte Geschwindigkeitssteigerung gebracht hat.
-
- Strings: ------------------------------------------------------------
- Standardprozeduren für MODULA-Strings, teilweise mit erweiterten
- Parametern. Zusätzlich Routinen für Benutzung von Einzelzeichen
- mit Strings.
- Das Modul ist zum überwiegenden Teil in Assembler geschrieben
- ( die MODULA-Quellen sind allerdings auch noch als Kommentar
- vorhanden ), sodaß die Prozeduren sehr schnell sind ( ich hoffe:
- auch richtig...)
-
- XStrings: -----------------------------------------------------------
- Weitere Prozeduren für Strings, hauptsächlich für die Suche inner-
- halb der Strings auf unterschiedliche Arten.
-
- LongSets: -----------------------------------------------------------
- Hiermit können Mengen mit bis zu 65535 Elementen verwendet werden;
- es sind alle grundlegenden Mengenoperationen vorhanden. Der Element-
- typ und damit die tatsächliche Anzahl der Mengenelemente kann (muß)
- im Definitionsmodul angegeben weden, worauf ein Übersetzungsvor-
- gang erfolgen muß. Voreingestellt ist der Typ CHAR mit 256 Elementen.
-
- Stacks, Queues: -----------------------------------------------------
- Universelle und effiziente Implementierung dieser Datenstrukturen
- mit allen grundlegenden Funktionen. Es können Daten beliebigen Typs
- verarbeitet werden und die Speicherverwaltung kann in Grenzen bei
- der Einrichtung beeinflußt werden. Es ist eine automatische Fehler-
- behandlung möglich.
-
- ConvertStr: ---------------------------------------------------------
- Hier sind Konvertierungen zwischen allen Standarddatentypen von
- MODULA - außer REAL und LONGREAL - und Strings enthalten.
- Es können auch BOOLEAN-, BITSET- und BYTE-Werte verarbeitet werden.
- Die gewünschte Feldbreite und z.T. auch die Zahlenbasis können ange-
- geben werden. Eine automatische Fehlerbehandlung ist möglich.
-
- MathBase: -----------------------------------------------------------
- Prozeduren für gezielten Zugriff auf Exponent und Mantisse von
- REAL- und LONGREAL-Zahlen, unter anderem auch als schnelle Multi-
- plikations- und Divisionsoperationen mit Zweierpotenzen zu gebrau-
- chen. Außerdem: SIGN, real, entier, round; auch für beide REAL-
- Typen.
-
- Screen: -------------------------------------------------------------
- Prozeduren zur Verwendung der VT52-Terminalemulation des ATARI und
- verschiedene Einstellungsoperationen ( Cursorblinken... ). Unter-
- schiedliche Prozeduren zur Ausgabe von Zeichen und Strings direkt
- aufs Terminal ( mit BIOS.Bconout-RAWCON und CON ) und umlenkbar
- ( GEMDOS.Cconout und Cconws ).
-
- Timer: --------------------------------------------------------------
- Ermöglicht Zeitmessungen mit 5 ms Auflösung.
-
- MEMORY: -------------------------------------------------------------
- Schnelles Kopieren, Austauschen, Füllen, Löschen und Vergleichen
- von typenlosen Speicherbereichen ( assemblerprogrammiert ).
-
- TRAPdefs: -----------------------------------------------------------
- Datenmodul mit den Trapdefinitionen, die für Betriebssystemaufrufe
- nötig sind. Sämtliche vorkommenden Kombinationen von Parameter-
- größen sind vorhanden.
-
- MACHINE: ------------------------------------------------------------
- Zugriff auf Systemvariablen, Änderung der Interruptpriorität...
- Nur für Spezialisten.
-
-
-
- Einige Module sind voneinander abhängig, d.h. die Übersetzungsreihenfolge
- muß beachtet werden:
-
-
- Chars Stacks Queues MACHINE Screen
- | | | / \ |
- V V V V V V
- ASCII MEMORY MEMORY MEMORY TRAPdefs TRAPdefs
-
-
- ConvertStr XStrings
- | / \
- V V V
- Chars Strings Chars
- | |
- V V
- ASCII ASCII
-
-
-
- Die Module "LongSets", "Stacks", "Queues" und "ConvertStr" wurden mit
- Testmodus übersetzt, die anderen ohne.
-
-
- Assemblerbenutzung:
- ========================================================================
-
- Hier folgen einige Informationen über die Variablen- und Parameter-
- benutzung auf Assemblerebene, soweit ich das durch eigene Versuche
- ermitteln konnte.
-
- Alle Variablen und Parameter beginnen an einer geraden Adresse, auch
- solche vom Typ CHAR, BOOLEAN oder Aufzählungstypen ( alles BYTE-Größe ).
-
- Die Parameterübergabe findet vollständig über den Stack statt, auch
- Funktionswerte werden nicht in einem Register, sondern auf dem Stack
- zurückgegeben.
- VALUE-Parameter ( außer den weiter unten besprochenen offenen Feldern )
- werden vom Aufrufer komplett auf den Stack kopiert, bei VAR-Parametern
- ists nur die Adresse.
- Die Parameter werden in der Reihenfolge ihres Auftretens in der Liste
- auf dem Stack abgelegt, d.h. der erste in der Liste stehende Parameter
- bzw. seine Adresse wird als erstes auf den Stack gebracht. Ist die
- aufgerufene Prozedur eine Funktion, wird der Platz für die Rückgabe
- des Funktionswertes vor allen Parametern auf dem Stack reserviert
- ( Der Funktionswert ist sozusagen der nullte Parameter ).
-
- Jede Prozedur räumt - neben den lokalen Variablen - ihre eigenen Para-
- meter vom Stack, sodaß der Aufrufer die von ihm abgelegten aktuellen
- Parameter nicht mehr entfernen muß, außer einem evtl. Funktionswert.
-
- Bei Prozedureintritt werden die Register d0-d7 und a0-a3 nicht geret-
- tet; wenn Werte in diesen Registern stehen, die nach dem Aufruf noch
- benötigt werden, müssen sie vom Aufrufer vor dem Aufruf auf dem
- Stack gesichert werden - normalerweise werden aber keine Werte in
- Registern gehalten außer innerhalb von Ausdrücken ( Zwischenwerte ).
-
- Die Register d0-d7,a0-a3 sind für die Verwendung von Assemblerpro-
- grammen frei, solange innerhalb einer Prozedur nicht mit einer Mischung
- aus MODULA- und INLINE-Code gearbeitet wird, ansonsten muß ein Dis-
- assemblerlisting über die Verwendung der Register im MODULA-Code
- Aufschluß geben.
- Die Register a4-a7 werden vom System benötigt und dürfen daher nicht
- verändert werden ( außer a7 als Parameter- und Sicherungsstack ).
-
- Wird eine verschachtelte ( lokale ) Prozedur aufgerufen, wird das
- Register a6 - der Zeiger auf den eigenen lokalen Datenraum - vom
- Aufrufer auf dem Stack abgelegt, sodaß die aufgerufene Prozedur
- Zugriff auf die lokalen Variablen und Parameter des Aufrufers hat.
-
-
- Nach dem BEGIN einer Prozedur sieht der Stack wie folgt aus:
-
-
- -- nur auf diesen Bereich hat die Prozedur direkten Zugriff
- |
- V ___________________________
- # -->| |<-- erster Parameter
- # | | akt. Parameter | ^
- # | | | |
- # | | | |
- # +---|---------------------------|<-- letzter Parameter
- # | | RTN-Adresse |
- # konst. | |---------------------------|
- # Offset |12 | Modulbasis/ A6-Aufrufer |
- # | |---------------------------|
- # | | altes A6 |
- # A6 -----+-->|---------------------------|
- # | | |<-- erste lokale Variable
- # neg. | | lokale Variablen | ^
- # Offsets| | | |
- # | | | |
- # -->|---------------------------|<-- letzte lokale Variable
- | |<-- erster Par. dieses Typs
- | VALUE-Parameter vom Typ | ^
- | ARRAY OF ... | |
- | | |
- A7 -------->|---------------------------|<-- letzter Par. dieses Typs
- : :
- : Platz für Proz.aufrufe :
- : :
- |
- |
- V
- fallende
- Adressen
-
-
- Bei offenen Feldparametern ( ARRAY OF Typ ) kann die Größe der
- Parameter erst zur Laufzeit bestimmt werden; vom Aufrufer wird daher
- vor der Adresse des Feldes noch der maximale Index ( HIGH(..) )
- auf den Stack gebracht. Es wird immer nur die Adresse eines offenen
- Feldes übergeben, egal ob es ein VALUE- oder VAR-Parameter ist, im
- Gegensatz zu Parametern aller anderen Typen.
- Ist es ein VALUE-Parameter, so wird das Feld von der aufgerufenen
- Prozedur vor seine lokalen Variablen auf den Stack kopiert, ent-
- sprechend wird die Adresse des nun lokalen Strings im Feld der
- aktuellen Parameter auf den entsprechenden Wert gesetzt; es wird nur
- über diese Adresse im akt.Parameter-Feld auf solche Feldparameter
- zugegriffen.
-
- Als Beispiel folgt hier ein Disassemblerlisting einer Prozedur
- mit einem VALUE-Parameter vom Typ ARRAY OF CHAR:
-
-
- MOVE.L A4,-(SP) ; Modulbasis des Aufrufers retten
- MOVEA.L $44(PC),A4 ; a4 -> eigene Modulbasis
- LINK A6,#-$2 ; Platz für lokale Variable
- MOVE.W $10(A6),D2 ; d2 := HIGH( string );
-
- ; --------- String in lokale Variable kopieren
-
- ADDQ.W #$1,D2 ; Länge des Feldes ( = HIGH(..)+1 )
- BTST #$0,D2 ; ungerade Anzahl ?
- BEQ.S copy ; B: nein, ok
- ADDQ.W #$1,D2 ; ja, gerade machen, damit gerade Adresse
- copy: SUBA.W D2,SP ; SP -> lokale Stringvariable
- MOVEA.L $C(A6),A3 ; a3 -> string
- MOVE.L SP,$C(A6) ; Adr. des lokalen Strings merken, die Adr.
- ; des Originalstrings wird nicht mehr gebraucht
- MOVEA.L SP,A2 ; a2 -> lokale Stringvariable
- SUBQ.W #$1,D2 ; wegen dbra
- copylp: MOVE.B (A3)+,(A2)+ ; String kopieren
- DBRA D2, copylp ;
-
- ; ---------------------------------------------
- ; Prozedurrumpf
- ; ---------------------------------------------
-
- procend:UNLK A6 ; lokale Variablen abbauen
- MOVEA.L (SP)+,A4 ; a4 -> Modulbasis des Aufrufers
- MOVEA.L (SP)+,A0 ; a0 -> RTN-Adr.
- ADDQ.L #$6,SP ; Parameter vom Stack entfernen
- JMP (A0) ; END Length;
-
-
- ------------------------------------------
-
-
- SYSTEM exportiert eine Pseudoprozedur zur Einbettung von Assem-
- blercode in den Quelltext: INLINE. Im Gegensatz zu INLINE eignet
- sich die Peudoprozedur CODE, die Teil der Schlüsselworte ist und
- nicht importiert werden darf, dazu, im Modulaprogrammtext Betriebs-
- systemaufrufe oder andere Funtionen, für die man TRAPs braucht,
- abzusetzen ( siehe DEF.Modul Process und TRAPdefs ).
- Hierzu plaziert man ein CODE-Statement direkt unter eine Prozedur-
- deklaration anstelle des Prozedurrumpfes ( gleiche Syntax wie
- das FORWARD-Statement ). Wird diese Prozedur aufgerufen, so
- werden zuerst die entsprechenden Prozedurparameter auf dem Stack
- abgelegt, und dann das 16-Bit-Befehlswort ausgeführt, das vom
- Compiler anstelle des Prozeduraufrufs in den Code eingebettet
- wurde; dieses Befehlswort kann sinnvollerweise nur ein TRAP-Befehl
- sein.
- Mit diesem Befehl lassen sich zum Beispiel die Betriebssystem-
- routinen benutzen.
- Zu beachten ist, daß die Parameter vom Compiler in der Reihenfolge
- ihres Autretens in der Parameterliste auf den Stack gebracht werden,
- d.h. umgekehrt wie bei 'C'-Compilern ( die Funktionsnummer ist also
- der LETZTE Parameter in der Liste ). Da die meisten Beispiele in
- Büchern für Betriebssystemaufrufe in 'C' gehalten sind, müssen die
- angegebenen Parameter in umgekehrter Reihenfolge geschrieben werden.
- Da die Betriebssystemroutinen meistens ihren Funktionswert in Regis-
- ter D0 zurückgeben, muß dieser anschließend noch mit REG( 0 ) und
- evtl. Typkonvertierung einer Variablen zugewiesen oder als Funktions-
- wert zurückgegeben werden.
-
-
- Beispiel: ( siehe z.B. auch Modul "Screen" )
-
-
- CONST Akku = 0; (* M68000-Register D0 *)
- Getrez = 4; (* Xbios-Funktionsnr. *)
-
- PROCEDURE getrez():CARDINAL;
-
- PROCEDURE XBIOS (Nr : CARDINAL); CODE(4E4EH); (* XBIOS-Trap *)
-
- BEGIN (* getrez *)
- XBIOS( Getrez );
- RETURN( SHORT( REG( Akku )));
-
- (* wird zu:
- * move.w #Getrez, -(sp)
- * trap #XBIOS
- * move.w d0, RETURN(a6)
- *)
- END getrez;
-
- Vorsicht ist geboten, falls der Aufruf einer solchen Prozedur in einer
- Schleife geschieht: da der Stack nicht automatisch korrigiert wird
- ( Parameter entfernen ), wächst er immer weiter an...
- Bei einem einmaligen Aufruf macht das nichts, da bei Prozedurende
- der bei Prozedureintritt gesicherte ( LINK ... ) Stackpointer
- wieder zurückgeschrieben wird ( UNLK ... ), und somit sämtliche
- eventuell auf dem Stack noch liegenden Werte wieder entfernt werden.
- ( Bei einem wiederholten Aufruf innerhalb der Prozedur wird der Stack
- am Ende der Prozedur natürlich auch wieder bereinigt, aber wenn z.B.
- in einer Schleife 1000-mal Parameter von vielleicht 20 Bytes auf dem
- Stack abgelegt werden, wächst womöglich der Stack in andere Speicher-
- bereiche hinein - einen Stacktest gibt es bei diesem Compiler ja leider
- nicht ):
-
- ------------------------------------------
-
- Bei eingeschaltetem Testmodus produziert der Compiler zusätzlichen Code
- zur Überprüfung von Index- und Bereichsüberschreitungen ( auch bei Unter-
- bereichstypen! ) und fehlenden RETURN-Anweisungen bei Funktionsprozeduren.
- Da der Compiler den Assemblercode nicht entschlüsseln kann, fügt er na-
- türlich auch keinen Code für die Index- oder Bereichsüberschreitung ein;
- am Ende einer Funktionsprozedur prozedur werden allerdings immer ein paar
- Bytes eingefügt, die einen Laufzeitfehler produzieren ( -> 'Funktionspro-
- zedur ohne RETURN'). Durch eine explizite RETURN-Anweisung wird dieser
- Code durch einen Sprungbefehl im wahrsten Sinne des Wortes umgangen.
- Codiert man nun in Assembler eine Funktionsprozedur, so muß entweder
- ein zusätzlicher Sprungbefehl eingebaut werden, was aber nicht so einfach
- ist, da das Sprungziel per INLINE nicht erreichbar ist - es liegt prak-
- tisch innerhalb der END-Anweisung, die zweite Möglichkeit wäre, den
- Funktionswert durch
-
- RETURN( VAL( Funtionstyp, REG( Register )))
-
- zurückzugeben. Schließlich kann man auch den Testmodus ausschalten, was
- nicht weiter tragisch ist, da in den Assemblercode sowieso keine weiteren
- Tests eingebaut werden ( können ). Hat man sowohl MODULA-Quelltext als
- auch INLINEs im Modul, sollten die MODULA-Teile unbedingt vorher MIT Test-
- modus ausgetestet werden.
-
-
- Compiler, Standardprozeduren und Laufzeitsystem
- ====================================================================
-
- Hier folgen einige Eigenheiten der oben angegebenen Komponenten.
-
-
- Das Pseudomodul 'SYSTEM' exportiert eine Funktion 'SHIFT':
-
- stdtyp1 := SHIFT( stdtyp2, shift );
-
- Für 'stdtyp' kann ein beliebiger Standarddatentyp oder ein Unterbereichs-
- typ außer REAL oder LONGREAL stehen. Die Funktion weist der Variablen
- 'stdtyp1' den um <shift> Bits nach rechts oder links verschobenen
- Wert von 'stdtyp2' zu.
-
- shift < 0 : um <shift> Bits nach rechts schieben
- shift > 0 : -"- links "
-
- <shift> wird MODULO 32 behandelt, d.h <shift> = 33 äq. <shift> = 1.
- Ist 'stdtyp2' vom Typ INTEGER, LONGINT oder ein Unterbereich vom
- Typ INTEGER, wird arithmetisch verschoben, sonst logisch.
-
- Beispiele für die Umsetzung des SHIFT-Befehls in Assemblercode:
-
- :
-
- VAR bs : BITSET;
- li : LONGINT;
-
- :
-
- bs := SHIFT( bs, 13 );
-
- (* wird zu:
- * move.w bs(a6), d2
- * lsl.w #5, d2
- * lsl.w #8, d2
- * move.w d2, bs(a6)
- *)
-
- li := SHIFT( li, -5 );
-
- (* wird zu
- * move.l li(a6), d2
- * asr.l #-5, d2
- * move.l d2, li(a6)
- *)
-
- Das funktioniert auch, wenn <shift> eine Variable ist; diese wird immer
- als Zahl mit Vorzeichen interpretiert, sodaß zur Laufzeit entschieden
- wird, in welche Richtung zu schieben ist.
-
- ------------------------------------------
-
- Folgende Standardprozeduren arbeiten mit INTEGER- statt der erwarteten
- CARDINAL-Werte
-
- ABS !!
- HIGH
- ORD
- LONG * bei Anwendung auf CARDINAL
- SHORT * -"- LONGCARD
- TRUNC
- TRUNCD
- FLOAT
- FLOATD
-
- Wobei mit 'arbeiten' der zur Uebersetzungszeit bekannte Funktionstyp
- gemeint ist ( Meldung: 'incompatible Operand Types' ); was dann zur
- Laufzeit produziert wird, ist manchmal etwas ganz anderes!
-
- Beispiel:
-
- card := SHORT( longcard );
-
- Ist der Testmodus eingeschaltet, wird überprüft, ob der Wert
- MAX(INTEGER) überschritten wird - d.h. SHORT soll nur INTEGER-
- Werte produzieren.
-
- longcard := LONG( card );
-
- Hier wird <card> vor der Zuweisung auf LONGCARD erweitert ( das
- höherwertige Wort wird gelöscht ), und auch bei eingeschaltetem
- Testmodus findet keine weitere Ueberprüfung statt, d.h. LONG
- liefert LONGCARD-Werte, obwohl der Compiler LONG als LONGINT-
- Funktion betrachtet ( longcard := longcard + LONG( card ); produ-
- ziert den Typfehler )!
-
-
- Wohl am Laufzeitmodul 'System' liegt es, daß FLOAT vom Compiler her auch
- negative Werte akzeptiert ( FLOAT und TRUNC werden in Aufrufe von
- System.FLOATs bzw. System.TRUNCs umgesetzt, TRUNCD und FLOATD in
- System.TRUNCd und System.FLOATd ) aber daraus Unsinn produziert;
- FLOATD hingegen verarbeitet auch negative Zahlen korrekt.
-
- Die Systemroutinen für TRUNC und FLOAT arbeiten übrigens, wie man aus
- dem Definitionsmodul ersehen kann, mit LONGINT-Parametern; hier entsteht
- ein neues Problem: Die automatische Längenanpassung bei Zuweisung und
- Übergabe als Value-Parameter:
-
- Bei der automatischen Anpassung der Längen von INTEGER und CARDINAL- auf
- LONG-Zahlen ist Vorsicht angebracht: Soll z.B. der Absolutwert einer
- INTEGER-Zahl einer LONGCARD-Zahl zugewiesen werden, könnte man schreiben:
-
- lc := ABS( int );
-
- Ist aber <int> = MIN(INTEGER), so wird der Wert durch ABS() nicht
- geändert, da es für diesen Wert keine entsprechende positive Zahl im
- Zweierkomplement gibt; was passiert? MIN(INTEGER) wird für die Zuweisung
- vorzeichenrichtig auf LONGINT erweitert, als Kardinalzahl interpretiert
- ist das aber MAX(LONGCARD) - MAX(INTEGER), und nicht etwa
- MAX(INTEGER) + 1, wie erwartet.
-
- Abhilfe:
-
- lc := ABS( LONG( int ));
-
- Da hier zuerst auf LONGINT erweitert wird und dann der Absolutwert genom-
- men wird, kann der Wertebereich nicht überschritten werden.
-
-
- Bei der Konvertierung von CARDINAL- und INTEGER-Zahlen und bei der Benut-
- zung von Standardprozeduren ist also Wachsamkeit angesagt - hier hilft
- oft nur Probieren.
-
- -------------------------------------------------
-
- Ach ja, das Laufzeitmodul: Die Division von LONGREAL-Zahlen wird, falls
- nicht gerade durch eine Zweierpotenz dividiert werden soll, durch einen
- Fehler mit schätzungsweise 32 statt 53 BIT Genauigkeit ausgeführt!
- Da unter anderem wohl auch der Compiler mit diesem Laufzeitmodul über-
- setzt wurde, lassen sich auch LONGREAL-Konstanten im Programmtext
- ( 1.23456789D bzw. 1.23456789D00 ) nur mit dieser Genauigkeit
- - ca. 10 Dezimalstellen - angeben, da bei der Umwandlung der Zeichen-
- ketten auch die Division benötigt wird.
-
- Aber auch bei der Behandlung von REAL-Konstanten kommt der Compiler
- ins Stolpern:
-
- real := 3.9999998 gibt den angegeben Wert, aber
- real := 3.9999999 gibt 6.0 !!!
-
- Da scheint wohl das Runden nicht so ganz zu funktionieren...
-
-
- -------------------------------------------------
-
- Bei Anwendung der Standardprozedur SIZE auf einen Parameter vom Typ
- ARRAY OF ... passiert recht merkwürdiges. Normalerweise wird die
- Prozedur ja nur dazu benutzt, die Speichergröße von Typen oder Varia-
- blen zu bestimmen, die bereits zur Übersetzungszeit bekannt sind
- ( vom Compiler wird dann nur eine Konstante eingesetzt ); wird sie
- aber z.B. innerhalb einer Prozedur auf einen Parameter vom Typ
- ARRAY OF CHAR angewendet:
-
- card := SIZE( string ); ,
-
- gibt es während des Übersetzens zunächst einen Busfehler! Nun ist ein
- Busfehler in einem Compiler eigentlich schon recht merkwürdig, wird aber
- die Meldung ignoriert und einfach CR gedrückt, so kann man sich nachher
- mittels Disassembler davon überzeugen, daß korrekter Code erzeugt wurde,
- nämlich der für:
-
- card := HIGH( string ) + 1;
-
- ----------------------------------------------
-
- Werden Stringvariablen mit einer ungeraden Anzahl Zeichen deklariert,
- z.B.:
-
- string := ARRAY [0..8] OF CHAR; ,
-
- so kann man diesen Strings auf der Ebene, in der sie deklariert wurden,
- um ein Zeichen längere Stringkonstanten zuweisen, ohne daß der Compiler
- meckert; die Funktion HIGH() liefert dagegen den richtigen Maximalindex.
- Verwirrung kann es schaffen, wenn im Hauptprogramm der Stringvariablen
- ein ( um ein Zeichen zu langer ) Text zugewiesen wurde, und diese Vari-
- able einer Prozedur als Parameter dient; dann bearbeitet die Prozedur
- den String nämlich bis zu dem mit HIGH() festgestellten - korrekten -
- Maximalindex, und das letzte Zeichen fehlt plötzlich, z.B. bei einer
- Ausgabe.
-
- -----------------------------------------------
-
- In der Importliste scheint der Compiler bei den Modulnamen nicht
- zwischen Groß- und Kleinbuchstaben zu unterscheiden, außer bei SYSTEM.
-
- -----------------------------------------------
-
- Während ein Programm unter Kontrolle des Laders läuft, leitet dessen
- Laufzeitsystem einige Exceptions auf eigene Prozeduren um, nach Beendi-
- gung des Laders werden die alten Adressen wiederhergestellt.
- Was bei Exceptions wie Adressfehler, Busfehler usw. ganz praktisch ist,
- erweist sich bei zumindest einer anderen Umlenkung als fatal: Der Trap
- Nr. 11 wird - vom Compiler in den Code eingebettet - dazu verwendet, in
- den Supervisormodus zu schalten. Das wird immer dann benötigt, wenn eine
- Modulpriorität angegeben wird, z.B.:
-
- IMPLEMENTATION MODULE Spezial[7];
-
- in diesem Fall wird nämlich bei jeglicher dynamischer Benutzung des
- Moduls, also Modulinitialisierung und Prozeduraufrufe, die Interruptebene
- der CPU auf den angegebenen Prioritätswert gesetzt - und dazu wird der
- Supervisormodus gebraucht.
- Eine Priorität ist z.B. beim mitgelieferten 'Process'.Modul angegeben.
- Wird aber nun durch den Linker ein selbständig lauffähiges Programm er-
- zeugt, setzt das Laufzeitsystem des Laders den Vektor für Trap #11 natür-
- lich nicht auf die Routine zur Umschaltung des Modus um. Da der Trap aber
- im Code steht, stürzt das Programm wegen Privilegverletzung ab, sobald
- der Initialisierungsteil eines solchen Moduls bei Programmstart ausge-
- führt wird.
- Abhilfe wäre ein kurzes Modul, das als erstes in jedes Programm impor-
- tiert wird, welches ein anderes Modul mit Prioritätsangabe direkt oder
- indirekt benutzt; in dessen Initialisierungsteil müßte ein kurzes
- Codestück stehen, das den Trap #11-Vektor auf eine dort stehende Pro-
- zedur umsetzt, die den Modus wechselt. Außerdem müßte dieses Modul eine
- Prozedur exportieren, die - bei Programmende aufgerufen - den alten
- Wert des Vektors wieder restauriert.
- Solange der Lader die Kontrolle über das System hat, führt der Vektor
- auf folgende kleine Routine ( disassembliert ):
-
-
- TOSUPER:
- move.w #$2700, SR ; Keinen IR während der Aktion zulassen
- ori.w #$2700, (SP) ; nach dem Trap auch durch nichts mehr
- ; unterbrechbar und im Supervisormodus
- rte ; Rückkehr zum Aufrufer
-
-
- Für den Code bei Modulen mit Priorität wird im übrigen der Befehl
-
- move SR, d*
-
- im Usermodus benutzt; das geht nur beim 68000, sitzt im ATARI ein
- 68010/68020, gibt das eine Privilegverletzung; Mit diesen Prozessoren
- kann also beim gegebenen Compiler die Prioritätsangabe nicht verwendet
- werden.
-
- Vom Modul 'Process' werden zusätzlich noch die Traps 3 und 4 auf eigene
- Routinen gelenkt; das geschieht jedoch, wie man sich durch einen Blick
- in den bei der ersten Version von 'LPR-Modula' mitgelieferten Quelltext
- überzeugen kann, ohne Rücksicht auf das, was vorher in den Vektoren
- drinstand; mithin lassen sich die ursprunglichen Werte auch nicht mehr
- restaurieren. Das ist zwar in der Praxis nicht weiter schlimm, da die
- von TOS nicht benutzten Traps wohl auch von anderen Programmen nicht
- verwendet werden, aber auszuschließen ist das auch nicht.
-
-
-
- Literatur:
- =========================================================================
-
- MODULA-2 - Software Components, JOHN WILEY & SONS
-
- Richard F. Sincovec
- Richard S. Wiener
-
-
-
- MODULA-2 - A Software Development Approach, JOHN WILEY & SONS
-
- Gary A. Ford
- Richard S. Wiener
-
-
-
- Algorithmen und Datenstrukturen mit Modula-2, B.G. Teubner
-
- Niklaus Wirth
-
-
-
- ATARI ST Profibuch, SYBEX
-
- Hans-Dieter Jankowski
- Julian F. Reschke
- Dietmar Rabich
-
-
-
- Zu guter Letzt:
- ========================================================================
-
- HK_LIB soll weiterentwickelt werden, da ich aber dummerweise auch
- noch studieren muß, kann es einige Zeit dauern, bis eine neue
- Version erscheint, entsprechende Anfragen sind daher zwecklos.
- Sobald der Umfang der Aenderungen oder Erweiterungen es recht-
- fertigt, wird eine neue Version veröffentlicht.
-
- Ich habe alle Prozeduren getestet; da mir dabei aber sicher diverse
- Grenzfälle entgangen sind, was besonders bei den Assemblerversionen
- leicht passiert, lehne ich jede Verantwortung für Bömbchen, ge-
- löschte Hauptspeicher, in denen vorher die noch nicht gesicherte
- Diplomarbeit stand, formatierte Festplatten usw. ab.
-
- Wenn jemand meint einen Fehler entdeckt zu haben, so kann er/sie/es
- mir ja mal schreiben ( am besten mit Beispiel ).
-
- Meine Adresse ist:
-
- Holger Kleinschmidt
- Promenadenstr. 11 b
- 1000 Berlin 45
-
-